<?php
 /**
 +------------------------------------------------------------------------------
 * FramkPHP 快速易用PHP框架
 +------------------------------------------------------------------------------
 * @package  Framk
 * @author   shawn fon <ucic5@163.com>
 +------------------------------------------------------------------------------
 */
class Cache{
	
	private $tablePre;//表前缀
	private $isCache;//
	private $cacheTime;//缓存时间
	private $DB;//数据库实例
	/**
	* 构造方法
	*/	
	public function __construct($cacheTime) {	

		$this->tablePre=$GLOBALS['C']['DB']['TablePre'];//获取配置文件中数据表前缀
		$this->isCache=$GLOBALS['C']['IsCache'];//获取配置文件中数据表前缀
		$this->DB=_class('DB');	//实例化数据操作类对象
		$this->cacheTime=$cacheTime;
	}
	/**
	* 魔术函数：回调数据查询操作方法：返回数据
	* @param $method  数据查询方法，框架DB.class.php中有findAll,findOne,countRecords,用户可自已增加方法
	* @param $args  查询方法中传入的参数，（数据表|SQL语句）
	* 使用实例：	$list= $this->C()->findAll('demo',"select * from TB order by id desc");
				$one= $this->C()->findOne('demo',"select * from TB where id='$id' ");
				$totalNum=$this->C()->countRecords('demo',"select count(*) from TB");
	*/	
	public function __call($method, $args){
		
		$_table= $args[0];
		$sql=$args[1];
		if(is_array($_table)){
			foreach($_table as $key=>$value){
			$sql= str_replace($key, $this->tablePre.trim(strtolower($value)),$sql);//$this->tablePre.$value为完整的表名
			}	
			$table= trim(strtolower($_table['TB1']));//取第一个表名为缓存子目录		
		}else{
			$table=trim(strtolower($_table));
			$sql= str_replace('TB', $this->tablePre.$table,$sql);	
		}
		$cacheFolder = $table.md5($table);	
		if( $this->isCache==true &&  $this->cacheTime!=-1 &&  $method=='findAll' ){//判断是否缓存数据
			if( !isset($_GET['CPAGE']) || $_GET['CPAGE']<=2  ){
				$cacheDir = _buildDir(CACHE.'DataCache'.S.$cacheFolder);//建立缓存目录
				$data=$this->fetchByCache($method,$sql,$cacheDir);//获取缓存数组
			}else{
				$data=$this->fetchByDB($method,$sql);
			}	
		}else{
			$data=$this->fetchByDB($method,$sql);//否则直接从数据库取得数据
		}
		return  $data;	
	}
	
	/**
	* 数据查询：命名缓存文件与时间文件，若缓存文件存在且没有过期则直接读取，否则生成缓存再读取；返回缓存文件内容
	*
	* @param $method  数据查询方法
	* @param $sql 查询语句
	* @param $cacheDir 缓存要放置的文件夹
	*/		
	private function fetchByCache($method,$sql,$cacheDir) {
 
		$cacheFile= $cacheDir.$_GET['ACTION'].$_GET['METHOD'].'_'.md5($sql).'.php';
		$timeFile= $cacheDir.basename($cacheDir);	
		if (! file_exists ( $timeFile )) $this->createFile($timeFile);		
		if (! file_exists ( $cacheFile )|| filemtime( $cacheFile )<= filemtime( $timeFile )){//判断是否要更新缓存	
			$data=$this->fetchByDB($method,$sql);
			if(empty($data))return $data;//如果为空不生成缓存，防止产生不必要的缓存文件
			$this->createFile($cacheFile,serialize($data));//生成缓存文件
			return $data;		
		}
		if($data = file_get_contents($cacheFile)){	//读取缓存
			$data=str_replace("<?php if(!defined('FRAMK')) exit('access denied');?>",'',$data);
			return unserialize($data);
		}else{
			_error('readCacheError');
		}
	}
	/**
	* 直接从数据库查询数据；返回查询的数据
	* @param $method  数据查询方法
	* @param $sql 查询语句
	*/		
	private function fetchByDB($method,$sql){
 
		if (method_exists ( $this->DB, $method )) {
			return $this->DB->$method($sql); 
		}else {
			_error('methodNotExist','Database->'.$method);
		}
	}	
	
	/**
	* 添加数据：返回值：新增数据ID
	*
	* @param $table  本次操作的数据表
	* @param $fields 数据插入的字符串
	* @param $DO 新增记录或替换记录
	*/		 
 	public function insert($table,$fields,$DO='INSERT') {	
		
		$table=trim(strtolower($table));
		$kv=$this->parseStr($fields,0);//重组部分SQL语句
		$finalTable=$this->tablePre.$table;//完整的数据表名称
		$sql = str_replace ( ',)', ' )', " $DO INTO $finalTable ($kv[0]) VALUES ($kv[1])" );
		$result=$this->DB->update($sql);
		if($this->isCache==true && $result>0){//如果操作成功并且使用缓存时，更新主时间文件
			$cacheFolder = $table.md5($table);
			$cacheDir = _buildDir ( CACHE.'DataCache'.S.$cacheFolder);
			$this->createFile($cacheDir.$cacheFolder);
		}							
		return $result;	
	}

 	/**
	* 替换数据
	*
	* @param $table  本次操作的数据表
	* @param $fields 数据插入的字符串
	*/		
 	public function replace($table,$fields) {			
		$this->insert($table,$fields,$DO='REPLACE');
	}

	/**
	* 修改数据：返回值：更新的数据记录数
	*
	* @param $table  本次操作的数据表
	* @param $fields 数据更新的字符串
	* @param $condition 更新条件 
	*/		
 	public function update($table,$fields,$condition) {	
		
		$setFields=$this->parseStr($fields,1);
		$where='WHERE '.$this->parseStr($condition,2);	//重组部分SQL语句
		$table=trim(strtolower($table));
		$finalTable=$this->tablePre.$table;		 
		$sql ="UPDATE $finalTable SET $setFields $where";
		$result=$this->DB->update($sql);					
		if($this->isCache==true && $result>0){//如果操作成功并且使用缓存时，更新主时间文件，如果缓存标识不为空，同时还更新与缓存文件相对应的时间文件
			$cacheFolder = $table.md5($table);
			$cacheDir = _buildDir ( CACHE.'DataCache'.S.$cacheFolder);
			$timeFile=$cacheDir.$cacheFolder;
			if (! file_exists ( $timeFile )|| filemtime( $timeFile)+$this->cacheTime< time() ){
				$cacheDir = _buildDir ( CACHE.'DataCache'.S.$cacheFolder);
				$this->createFile($cacheDir.$cacheFolder);				
			}		
		}
		return $result;	
	}		

	/**
	* 删除数据：返回值：删除数据的记录数
	*
	* @param $table  本次操作的数据表
	* @param $condition 删除数据的条件 
	*/			
	public function delete($table,$condition) {	

		$where='WHERE '.$this->parseStr($condition,2);//重组部分SQL语句	
		$table=trim(strtolower($table));
		$finalTable=$this->tablePre.$table;
		$sql = "DELETE FROM $finalTable $where";	
		$result=$this->DB->update($sql);
		if($this->isCache==true && $result>0){//如果操作成功并且使用缓存时，更新主时间文件，如果缓存标识不为空则删除缓存文件与时间文件
			$cacheFolder = $table.md5($table);
			$cacheDir = _buildDir ( CACHE.'DataCache'.S.$cacheFolder);
			$this->createFile($cacheDir.$cacheFolder);							
		}							
		return $result;	
	}
	
	/**
	* 重组SQL部分语句
	*
	* @param $str  要解析的字符串
	* @param $case 操作类型
	*/		
	 private function parseStr($fields,$case){
	
		$reVal_1 = ''; $reVal_2 = '';
		foreach ($fields as $filedName=>$val) {		
			$key = trim($filedName);
			if(empty($key))_error('DBFieldEmpty_Error',$filelds);
			switch ($case) {
			case 0: //增加数据，
				$reVal_1 .="$key,";
				$reVal_2 .="'$val',";
				break;
			case 1: //更新数据 SET 
				$reVal_1 .="$key='$val',";
				break;
			case 2: //数据操作的条件 WHERE  
				$reVal_1 .="$key='$val' AND ";
				break;				
			}
		}
		unset($fields);	
		switch ($case) {
		case 0: 
			return array($reVal_1,$reVal_2);
		case 1: 
			return substr($reVal_1,0,-1);//截去掉，号
		case 2: 
			return substr($reVal_1,0,-4);//截去掉 AND 空格	
		}		
	 }	
	 
	/**
	* 文件生成：用于缓存文件与时间文件的生成
	*
	* @param $file  写入数据的文件
	* @param $content 写入的内容 
	*/	
 	private function createFile($file,$content='') {
		$content="<?php if(!defined('FRAMK')) exit('access denied');?>".$content;
		if (! file_put_contents ( $file, $content)) _error ('writeCacheError',$file);
	}
	
 /*  +------------------------------------------------------------------------------ */		
	} //
?>